import { ObjectTool, Predicate } from '@es-tool/core'
import { UpdateRes } from 'database-ql/src/result-types'
import { Db } from 'laf-client-sdk'
import { Sealed } from './annotation/Sealed'
import { ResponseStatusException } from './exception/ResponseStatusException'
import { LafWrapperConfig } from './LafWrapperConfig'
import { Logger } from './log/Logger'
import { QueryChain } from './QueryChain'

// noinspection JSUnusedGlobalSymbols
/**
 * UpdateChainWrapper
 * @author LL
 * @date 2022-01-11 上午 10:10
 **/
@Sealed
export class UpdateChainWrapper<E = Record<string, any>> extends QueryChain<E> {
    /**
     * 引用的表名
     * @type {string}
     * @private
     */
    private readonly tableName: string
    /**
     * 日志记录器
     * @type {Logger}
     * @private
     */
    private log: Logger

    private tempUpdate: Record<keyof E, any> = {} as any

    /**
     * 构造
     * @param {string} tableName
     * @param {Db | null} database
     */
    constructor(tableName: string, database: Db | null = null) {
        super(database as Db ?? LafWrapperConfig.database())
        this.tableName = tableName
        this.log = LafWrapperConfig.LoggerFactory.getLogger(`UpdateWrapper#${ this.tableName }`)
    }

    /* 更新参数构建 ----------------------------------------------------------------- */

    /**
     * 设置更新属性
     * @param column 列名
     * @param value 值
     * @return {UpdateChainWrapper<E>} 返回自身
     */
    public set<C extends keyof E>(column: C, value: E[C]): UpdateChainWrapper<E> {
        this.tempUpdate[column] = value
        return this
    }

    /**
     * 如果条件满足, 设置更新属性
     * @param {Predicate} predicate 条件谓词 输入值 value
     * @param column 列名
     * @param value 值
     * @return {UpdateChainWrapper<E>} 返回自身
     */
    public setIf<C extends keyof E>(predicate: Predicate<E[C]>, column: C, value: E[C]): UpdateChainWrapper<E> {
        if (predicate(value)) {
            this.tempUpdate[column] = value
        }
        return this
    }

    /**
     * 自增 size
     * @param {keyof E} column
     * @param {number} size
     * @returns {UpdateChainWrapper<E>}
     */
    public inc(column: keyof E, size: number = 1): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.inc(size)
        return this
    }

    /**
     * 自乘 size
     * @param {keyof E} column
     * @param {number} size
     * @returns {UpdateChainWrapper<E>}
     */
    public mul(column: keyof E, size: number = 1): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.mul(size)
        return this
    }

    /**
     * 删除属性
     * @param {keyof E} column
     * @returns {UpdateChainWrapper<E>}
     */
    public remove(column: keyof E): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.remove()
        return this
    }

    /**
     * 数组尾部追加
     * @param {keyof E} column
     * @param value
     * @returns {UpdateChainWrapper<E>}
     */
    public push(column: keyof E, value: any): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.push(value)
        return this
    }

    /**
     * 数组尾部删除
     * @param {keyof E} column
     * @returns {UpdateChainWrapper<E>}
     */
    public pop(column: keyof E): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.pop()
        return this
    }

    /**
     * 数组头部添加
     * @param {keyof E} column
     * @param value
     * @returns {UpdateChainWrapper<E>}
     */
    public unshift(column: keyof E, value: any): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.push(value)
        return this
    }

    /**
     * 数组头部删除
     * @param {keyof E} column
     * @returns {UpdateChainWrapper<E>}
     */
    public shift(column: keyof E): UpdateChainWrapper<E> {
        this.tempUpdate[column] = this.database.command.shift()
        return this
    }

    /* 更新参数结束 --------------------------------------------------- */

    /**
     * 最终操作: 更新
     * @param {boolean} multi 是否批量更新, 默认 false;  需要 multi 权限
     * @param {boolean} merge 合并 默认 true
     * @param {boolean} upsert upsert 默认 false
     * @returns {Promise<string | number | boolean>} 更新成功,返回 true;  upsert 模式 返回 upsertId
     */
    public async update(multi: boolean = false, merge: boolean = true, upsert: boolean = false): Promise<string | number | boolean> {
        const res: UpdateRes = await this.database
            .collection(this.tableName)
            .where(this.getWhereArg())
            .update(this.tempUpdate)

        if (ObjectTool.isNotEmpty(res) && res?.ok) {
            const { matched, updated, upsertId } = res
            this.log.debug(`Update 匹配: ${ matched }, 更新: ${ updated } upsertId: ${ upsertId } `)

            return upsert ? upsertId : true
        }
        throw new ResponseStatusException(res.error)
    }

}
